import { _decorator, Component, Node, v3, Vec3 } from 'cc';
import { BaseView } from '../../../scripts/libs/rxjs/cc3/BaseView';
import { GamePanel } from '../ui/GamePanel/GamePanel';
import { genId, StageInfo } from '../game/basic';
import { Subscription } from 'rxjs';
import { size } from 'cc';
import { Camera } from 'cc';
import { linear } from '../../../scripts/libs/utils/math';
import { Timer } from '../../../scripts/libs/rxjs/cc3/CreatorSchedule';
import { Ground } from './Ground';
import { ResourceHelper } from '../ResourceHelper';
import { Block } from './Block';
import { KeyDown } from '../../../scripts/libs/rxjs/cc3/KeyEvent';
import { KeyCode } from 'cc';
import { gm } from '../../../scripts/managers/GameManager';
import { udm } from '../../../scripts/managers/UserDataManager';
import { nullifySubscription } from '../../../scripts/libs/rxjs/utils';
import { DevConfig } from '../../../scripts/managers/DevConfig';
import { lm } from '../LobbyManager';
import { FlowStatus } from '../Enums';
import { GameLosePanel } from '../ui/GameLosePanel/GameLosePanel';
import { ParticleSystem } from 'cc';
import { UITransform } from 'cc';
import { mat4 } from 'cc';
const { ccclass, property } = _decorator;

const _m0 = mat4();

@ccclass('Stage3dView')
export class Stage3dView extends BaseView {

    gamePanel: GamePanel = null;

    stageInfo: StageInfo;

    @property(Camera)
    camera: Camera = null;

    @property(Node)
    groundContainer: Node = null

    @property(Node)
    blockContainer: Node = null

    @property(Node)
    fxContainer3dNode: Node = null;

    useObserves() {
        return [
            KeyDown().subscribe((_: KeyCode) => {
                if (_ == KeyCode.KEY_W || _ == KeyCode.ARROW_UP) {
                    this.roll(1);
                } else if (_ == KeyCode.KEY_S || _ == KeyCode.ARROW_DOWN) {
                    this.roll(3);
                } else if (_ == KeyCode.KEY_A || _ == KeyCode.ARROW_LEFT) {
                    this.roll(2);
                } else if (_ == KeyCode.KEY_D || _ == KeyCode.ARROW_RIGHT) {
                    this.roll(0);
                }
            })
        ];
    }

    onDestroyView(): void {
        nullifySubscription.call(this, '_delay_in_lose_panel');
    }

    static Space = v3(1, 1, 1);

    static getPosByOffsetRowCol(row: number, col: number) {
        return v3(col * this.Space.x, 0, row * this.Space.z);
    }

    get Width(): number {
        return this.stageInfo?.width ?? 0;
    }

    get Height(): number {
        return this.stageInfo?.height ?? 0;
    }
    get TotalCellCount() {
        if (this.stageInfo != null) {
            return this.Width * this.Height;
        }
        return 0;
    }
    isEdgeWall(idx: number) { // 这个格子是墙？最边缘
        let [r, c] = this.getRowColPos(idx);
        return r == 0 || r == this.Height - 1 || c == 0 || c == this.Width - 1;
    }
    getIdxByRowCol(rc: number[]) {
        return rc[0] * this.Width + rc[1];
    }
    getRowColPos(idx: number): number[] {
        return [Math.floor(idx / this.Width), idx % this.Width]; // 得到行列坐标，上到下，左到右
    }
    getPositionByIdx(idx: number) {
        let [row, col] = this.getRowColPos(idx);
        return this.getPositionByRowCol(row, col);
    }
    getPositionByRowCol(row: number, col: number) {
        let x = col * Stage3dView.Space.x - Stage3dView.Space.x * this.Width / 2 + Stage3dView.Space.x / 2;
        let z = row * Stage3dView.Space.z - Stage3dView.Space.z * this.Height / 2 + Stage3dView.Space.z / 2;
        return v3(x, 0, z);
    }
    getRowColByPos(position: Vec3) {
        let x = Math.floor((position.x + Stage3dView.Space.x * this.Width / 2) / Stage3dView.Space.x);
        let z = Math.floor((position.z + Stage3dView.Space.z * this.Height / 2) / Stage3dView.Space.z);
        return [z, x];
    }
    getIdxByPos(position: Vec3) {
        let [row, col] = this.getRowColByPos(position);
        if (row < 0 || row >= this.Height) return null;
        if (col < 0 || col >= this.Width) return null;
        return this.getIdxByRowCol([row, col]);
    }
    getAdjoin1(posId) {
        let [row, col] = this.getRowColPos(posId);
        let list = [
            { data: [row, col + 1], direction: 0 },
            { data: [row - 1, col], direction: 1 },
            { data: [row, col - 1], direction: 2 },
            { data: [row + 1, col], direction: 3 }
        ];
        return list.filter(_ => {
            let [row, col] = _.data;
            return row >= 0 && row < this.Height && col >= 0 && col < this.Width;
        }).map(_ => {
            let [row, col] = _.data;
            return { idx: this.getIdxByRowCol([row, col]), direction: _.direction };
        });
    }
    getDirection8(posId: number, direction8: number) { // 获得8个方向的相邻格子id
        let [row, col] = this.getRowColPos(posId);
        if (direction8 == 0) [row, col] = [row, col + 1];
        else if (direction8 == 1) [row, col] = [row - 1, col + 1];
        else if (direction8 == 2) [row, col] = [row - 1, col];
        else if (direction8 == 3) [row, col] = [row - 1, col - 1];
        else if (direction8 == 4) [row, col] = [row, col - 1];
        else if (direction8 == 5) [row, col] = [row + 1, col - 1];
        else if (direction8 == 6) [row, col] = [row + 1, col];
        else if (direction8 == 7) [row, col] = [row + 1, col + 1];
        if (row >= 0 && row < this.Height && col >= 0 && col < this.Width) { // 在范围内
            return this.getIdxByRowCol([row, col]);
        }
        return null;
    }

    localToWorldPos(pos: Vec3) {
        this.blockContainer.getWorldMatrix(_m0);
        return pos.transformMat4(_m0);
    }

    idxOffsetByDirection(idx: number, direction: number) {
        let [row, col] = this.getRowColPos(idx);
        if (direction == 0) {
            col++;
        } else if (direction == 1) {
            row--;
        } else if (direction == 2) {
            col--;
        } else if (direction == 3) {
            row++;
        }
        if (row < 0 || row >= this.Height) return null;
        if (col < 0 || col >= this.Width) return null;
        return this.getIdxByRowCol([row, col]);
    }


    getGridDistance(posId0: number, posId1: number) {
        let [row0, col0] = this.getRowColPos(posId0);
        let [row1, col1] = this.getRowColPos(posId1);
        return Math.abs(row0 - row1) + Math.abs(col0 - col1);
    }

    wallSet: Set<number> = null;
    groundMap = new Map<number, Ground>();
    blockMap = new Map<number, Block>();

    loadStage(stageInfo: StageInfo) {
        this.stageInfo = stageInfo;
        this.wallSet = new Set(stageInfo.walls);

        gm.isWin.value = false;
        gm.isLose.value = false;

        this.recycleGround();
        this.genGround();
        this.recycleBlock();
        this.genBlock();

        this.startStageCameraAnim(() => {
            this.log('complete loadStage!');
            gm.firstTouched.value = true;
        });
    }

    scaleSize = 1;
    targetOrthoHeight = 10;
    _sub_camera_anim: Subscription = null;
    startStageCameraAnim(completeCallback: () => void, waitStart = false) {
        let parentSize = size(4, 7);
        this.log('@parentSize', parentSize);

        let stageWidth = this.stageInfo.width - 1;
        let stageHeight = this.stageInfo.height - 1;

        // 检查缩放应该首先依据宽还是高
        let whRateStage = stageWidth / stageHeight;
        let whRateContainer = parentSize.width / parentSize.height;
        let scale = 1;
        if (whRateStage >= whRateContainer) { // stage的宽受限，应根据宽度进行缩放
            let widthMaxCount = parentSize.width / Stage3dView.Space.x;
            scale = widthMaxCount / stageWidth;
        } else if (whRateStage < whRateContainer) { // stage的高受限，应根据高度进行缩放
            let heightMaxCount = parentSize.height / Stage3dView.Space.z;
            scale = heightMaxCount / stageHeight;
        }
        this.scaleSize = scale;
        this.log('scaleSize', this.scaleSize);
        let targetOrthoHeight = 2.5 / this.scaleSize;
        this.targetOrthoHeight = targetOrthoHeight;
        this.log('@targetOrthoHeight', targetOrthoHeight);
        this.camera.projection = Camera.ProjectionType.PERSPECTIVE;
        let cameraY = linear(6.3, 12, 22, 38, targetOrthoHeight);
        let cameraZ = cameraY / Math.tan(Math.PI / 3);
        this.camera.node.setPosition(0, cameraY, cameraZ);
        this.camera.node.parent.setScale(1, 1, 1);
        this.camera.orthoHeight = targetOrthoHeight; // 数值越大，显示的范围越大
        this.camera.node.parent.setScale(1, 1, 1);
        let cameraTarget = this.camera.node.parent;
        cameraTarget.setPosition(0, 0, 0);
        // cameraTarget.setRotationFromEuler(0, 0, 0);
        this._sub_camera_anim = Timer(.1, this.node).subscribe(_ => {
            completeCallback && completeCallback();
            // gm.stageViewCameraAnimCompleted.execute();
        });
    }

    recycleGround() {
        this.groundContainer.children.map(_ => _).forEach(_ => {
            _.getComponent(Ground).recycle();
            ResourceHelper.GroundPool.putNode(_);
        });
        this.groundContainer.destroyAllChildren();
    }

    addGround(posId: number) {
        let node = ResourceHelper.GroundPool.getNode();
        let ground = node.getComponent(Ground);
        ground.groundId.value = posId;
        ground.reset();
        this.groundMap.set(posId, ground);
        node.setParent(this.groundContainer);
        let pos = this.getPositionByIdx(posId);
        node.setPosition(pos);
        return ground;
    }

    genGround() {
        for (let i = 0; i < this.TotalCellCount; i++) {
            if (!this.wallSet.has(i)) {
                this.addGround(i);
            }
        }
    }

    blockDefaultY = .5; // block默认 y 值

    recycleBlock() {
        this.blockContainer.children.map(_ => _).forEach(_ => {
            _.getComponent(Block).recycle();
            ResourceHelper.BlockPool.putNode(_);
        });
        this.blockContainer.destroyAllChildren();
    }

    addBlock(posId: number, blockId?: number) {
        let node = ResourceHelper.BlockPool.getNode();
        let block = node.getComponent(Block);
        block.stage3dView = this;
        block.blockId.value = blockId ?? genId<Block>(this.blockMap, _ => _.blockId.value);
        block.posId.value = posId;
        block.reset();
        this.blockMap.set(block.blockId.value, block);
        node.setParent(this.blockContainer);
        let pos = this.getPositionByIdx(posId);
        pos.y += this.blockDefaultY;
        node.setPosition(pos);
        return block;
    }

    genBlock() {
        for (let blockInfo of this.stageInfo.blocks) {
            if (blockInfo.color == 3) {
                let block = this.addBlock(blockInfo.pos, blockInfo.id);
                block.valid.value = true;
            }
        }
    }

    roll(direction: number) {
        for (let [blockId, block] of this.blockMap) {
            this.blockRoll(block, direction);
        }
    }

    blockRoll(block: Block, direction: number) {
        if (block.valid.value) {
            let offsetDirection = this.idxOffsetByDirection(block.posId.value, direction);
            // if (offsetDirection != null) {
            if (!block.rolling.value) {

                // let pos = this.getPositionByIdx(block.posId.value);
                // pos.y += this.blockDefaultY;
                // this.localToWorldPos(pos);

                this.genBlockEdgeParticle(block.node.worldPosition);

                block.roll(direction, () => {
                    this.log('after roll!');

                    if (offsetDirection == null || this.wallSet.has(offsetDirection)) {
                        // TODO:  播放掉落动画
                        block.drop(() => {
                            this.log('after drop!');
                            this.determineLose('drop_dead');
                        });


                    } else {

                        block.posId.value = offsetDirection;

                        if (block.nextRollDirection.value != null) {
                            let _nextDirection = block.nextRollDirection.value;
                            block.nextRollDirection.value = null;
                            this.blockRoll(block, _nextDirection);
                        }
                    }
                });
            } else {
                block.nextRollDirection.value = direction;
            }
            // }
        }
    }

    _delay_in_lose_panel: Subscription = null;
    determineLose(reason: string) {
        if (!gm.isLose.value) {
            gm.isLose.value = true;
            udm.gameLosePopup(this.stageInfo, reason as any);

            gm.bgmFadeReason.add('pre_lose_panel');

            this._delay_in_lose_panel = Timer(.5, this.node).subscribe(_ => {
                let callback = _ => {
                    if (_ == 'revive') {
                        gm.isLose.value = false;
                        gm.continueCount.value++;
                        // gm.firstTouched.value = false;
                        gm.addTime.value += DevConfig.SECOND_LOSE_CONTINUE;
                    } else if (_ == 'retry') {
                        this.gamePanel.loadStage();
                    } else if (_ == 'exit') {
                        lm.flowStatus.value = FlowStatus.Lobby;
                    } else if (_ == 'edit') {
                        lm.flowStatus.value = FlowStatus.EditorGame; // 返回编辑模式
                    }
                };
                GameLosePanel.StartProcess((panel: GameLosePanel) => {
                    panel.stageView = this;
                }).subscribe(callback.bind(this));
            });
        }
    }


    genBlockEdgeParticle(worldPos: Vec3) {
        let node = ResourceHelper.BlockEdgeParticle.getNode();
        node.clearSubscriptions();
        node.setParent(this.fxContainer3dNode);
        node.setWorldPosition(worldPos);
        let ps = node.getComponent(ParticleSystem);
        ps.play();
        Timer(1, node).subscribe(_ => {
            ps.stop();
            node.clearSubscriptions();
            ResourceHelper.BlockEdgeParticle.putNode(node);
        }).addTo(node);
    }

}

